﻿using System;
using System.Linq;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Trentacular.SharePoint.Features.Web.UI;
using Trentacular.SharePoint.Features.Web.UI.WebParts;

namespace Trentacular.SharePoint.Features.Web.UI.UserControls
{
    public partial class ListSelector : BaseSPUserControl, IWebPartEditor<ListViewWebPart>
    {
        #region Effective Properties

        private bool IsRemoteList
        {
            get { return GetViewStateValue<bool>("IsRemoteList"); }
            set { ViewState["IsRemoteList"] = value; }
        }

        private Guid? RemoteSiteId
        {
            get { return GetViewStateValue<Guid?>("RemoteSiteId"); }
            set { ViewState["RemoteSiteId"] = value; }
        }

        private Guid? RemoteWebId
        {
            get { return GetViewStateValue<Guid?>("RemoteWebId"); }
            set { ViewState["RemoteWebId"] = value; }
        }
        private Guid? ListId
        {
            get { return GetViewStateValue<Guid?>("ListId"); }
            set { ViewState["ListId"] = value; }
        }

        private Guid? DefaultViewId
        {
            get { return GetViewStateValue<Guid?>("DefaultViewId"); }
            set { ViewState["DefaultViewId"] = value; }
        }

        #endregion

        #region Helper Methods

        private T GetViewStateValue<T>(string key)
        {
            var value = ViewState[key];
            if (value == null)
                return default(T);

            return (T)value;
        }

        private bool IsInvalidSite = false;

        private void Invalidate(string message)
        {
            pnlRemoteSiteValidationMessage.Visible = true;
            pnlRemoteSiteValidationMessage.Controls.Add(new Literal
            {
                Text = message
            });

            IsInvalidSite = true;
        }

        private SPList GetSelectedList(SPWeb web)
        {
            var strSelectedListId = ddlList.SelectedValue;
            var selectedListId = string.IsNullOrEmpty(strSelectedListId) ? ListId : new Guid(strSelectedListId);

            SPList firstList = null;
            foreach (SPList list in web.Lists)
            {
                if (list.Hidden)
                    continue;

                if (firstList == null)
                    firstList = list;

                if (!selectedListId.HasValue)
                    break;

                if (list.ID.Equals(selectedListId.Value))
                    return list;
            }

            return firstList;
        }

        private SPView GetSelectedView(SPList list)
        {
            var strSelectedViewId = ddlView.SelectedValue;
            var selectedViewId = string.IsNullOrEmpty(strSelectedViewId) ? DefaultViewId : new Guid(strSelectedViewId);
            foreach (SPView view in list.Views)
            {
                if (!ListViewWebPart.IsViewAvailableForSelection(view))
                    continue;

                if (!selectedViewId.HasValue)
                    break;

                if (view.ID.Equals(selectedViewId.Value))
                    return view;
            }

            return list.DefaultView;
        }

        private bool IsStateInitialized = false;

        private void EnsureState()
        {
            if (IsStateInitialized || IsInvalidSite)
                return;

            // Get Remote Location type
            var selectedLocation = rblSiteLocation.SelectedValue;
            IsRemoteList = string.IsNullOrEmpty(selectedLocation) ? false : int.Parse(selectedLocation) > 0;

            // If Remote Site is specified
            if (IsRemoteList)
            {
                // Get Remote Site Url
                var remoteSiteUrl = txtRemoteSiteUrl.Text;

                // Validate Remote Site URL
                if (string.IsNullOrEmpty(remoteSiteUrl))
                {
                    Invalidate("Remote Site URL is required");
                    return;
                }

                try
                {
                    using (var site = new SPSite(remoteSiteUrl))
                    {
                        using (var web = site.OpenWeb())
                        {
                            RemoteSiteId = site.ID;
                            RemoteWebId = web.ID;

                            // Now validate list and view selections in the remote web
                            ValidateListAndView(web);
                        }
                    }
                }
                catch (Exception e)
                {
                    Invalidate(e.Message);
                    return;
                }
            }
            else
            {
                // Now validate list and view selections in the local web
                ValidateListAndView(Web);
            }

            IsStateInitialized = true;
        }

        private void ValidateListAndView(SPWeb web)
        {
            // Get the selected list if present, otherwise the first list in the web
            var selectedList = GetSelectedList(web);

            // Verify the site has a list available for selection
            if (selectedList == null)
            {
                Invalidate("This site has no lists available for display");
                return;
            }

            // Set the selected list id to our effective property
            ListId = selectedList.ID;

            var selectedView = selectedList.DefaultView;
            var strSelectedViewId = ddlView.SelectedValue;
            var selectedViewId = string.IsNullOrEmpty(strSelectedViewId) ? DefaultViewId : new Guid(strSelectedViewId);
            foreach (SPView view in selectedList.Views)
            {
                if (!ListViewWebPart.IsViewAvailableForSelection(view))
                    continue;

                if (view.ID.Equals(selectedViewId))
                {
                    selectedView = view;
                    break;
                }
            }

            // Set the selected view id to our effective property
            DefaultViewId = selectedView.ID;
        }

        private void PerformDataBinding(SPWeb web)
        {
            // No lonegr need to validate since we are calling EnsureState
            // ValidateListAndView(web);

            // DataBind List DDL
            ddlList.DataSource = web.Lists.Cast<SPList>()
                .Where(l => !l.Hidden)
                .Select(l => new { ID = l.ID, Title = l.Title })
                .ToArray();
            
            ddlList.DataBind();

            if (ListId.HasValue)
            {
                var strListId = ListId.Value.ToString();
                if (ddlList.Items.FindByValue(strListId) != null)
                    ddlList.SelectedValue = strListId;
            }
            else
            {
                ddlList.SelectedIndex = 0;
            }

            // DataBind View DDL
            var selectedList = GetSelectedList(web);
            ddlView.DataSource = selectedList.Views.Cast<SPView>()
                .Where(v => ListViewWebPart.IsViewAvailableForSelection(v))
                .Select(v => new { ID = v.ID, Title = v.Title })
                .ToArray();

            ddlView.DataBind();

            if (DefaultViewId.HasValue)
            {
                var strViewId = DefaultViewId.Value.ToString();
                if (ddlView.Items.FindByValue(strViewId) != null)
                    ddlView.SelectedValue = strViewId;
            }
        }

        #endregion

        #region Page Events

        protected void Page_PreRender(object sender, EventArgs e)
        {
            EnsureState();

            rblSiteLocation.SelectedValue = IsRemoteList ? "1" : "0";
            phRemoteSiteUrl.Visible = IsRemoteList;

            // Set the Site Url if it isn't already present
            if (IsRemoteList)
            {
                if (!IsInvalidSite)
                {
                    try
                    {
                        using (var site = new SPSite(RemoteSiteId.Value))
                        {
                            using (var web = site.OpenWeb(RemoteWebId.Value))
                            {
                                txtRemoteSiteUrl.Text = web.Url;
                                PerformDataBinding(web);
                            }
                        }
                    }
                    catch
                    {
                        // Since we failed to resolve the remote site, we hide the list and view selectors
                        IsInvalidSite = true;
                    }
                }
            }
            else
            {
                PerformDataBinding(Web);
            }

            phListAndView.Visible = !IsInvalidSite;
        }

        #endregion

        #region IWebPartEditor<ListViewWebPart> Members

        public ListViewWebPart ParentWebPart { get; set; }

        public bool ApplyChanges()
        {
            EnsureState();

            if (IsInvalidSite)
                return false;

            ParentWebPart.IsRemoteList = IsRemoteList;
            ParentWebPart.RemoteSiteId = RemoteSiteId;
            ParentWebPart.RemoteWebId = RemoteWebId;
            ParentWebPart.ListId = ListId;
            ParentWebPart.DefaultViewId = DefaultViewId;

            return true;
        }

        public void SyncChanges()
        {
            IsRemoteList = ParentWebPart.IsRemoteList;
            RemoteSiteId = ParentWebPart.RemoteSiteId;
            RemoteWebId = ParentWebPart.RemoteWebId;
            ListId = ParentWebPart.ListId;
            DefaultViewId = ParentWebPart.DefaultViewId;

            IsStateInitialized = true;
        }

        #endregion
    }
}